package com.cloud.pole.gate.filter;

import com.alibaba.fastjson.JSON;
import com.cloud.pole.api.log.LogApi;
import com.cloud.pole.api.permission.PermissionApi;
import com.cloud.pole.common.constant.CommonConstants;
import com.cloud.pole.common.context.BaseContextHandler;
import com.cloud.pole.common.exception.BaseException;
import com.cloud.pole.common.exception.auth.UserTokenException;
import com.cloud.pole.common.msg.ApiRestResponse;
import com.cloud.pole.common.msg.auth.TokenErrorResponse;
import com.cloud.pole.common.msg.auth.TokenForbiddenResponse;
import com.cloud.pole.common.util.ClientUtil;
import com.cloud.pole.common.util.jwt.IJWTInfo;
import com.cloud.pole.common.util.jwt.JWTInfo;
import com.cloud.pole.common.util.jwt.JwtTokenUtil;
import com.cloud.pole.feign.ILogService;
import com.cloud.pole.feign.IMerchantUserService;
import com.cloud.pole.gate.util.DBLog;
import com.cloud.pole.feign.IAdminUserService;
import com.cloud.pole.feign.IBaseUserService;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.entity.ContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.*;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;


@Component
@Slf4j
public class AdminAccessFilter extends ZuulFilter {
	@Autowired
	@Lazy
	private IAdminUserService adminUserService;
	@Autowired
	@Lazy
	private IMerchantUserService merchantUserService;
	@Autowired
	@Lazy
	private ILogService logService;
	@Autowired
	@Lazy
	private IBaseUserService userService;
	
    @Value("${gate.ignore.startWith}")
    private String startWith;
    
    //不做权限控制界面
    @Value("${gate.admin.ignore.permission}")
    private String ignorePermission;
    
    @Value("${zuul.prefix}")
    private String zuulPrefix;

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 1;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }
    

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        final String requestUri = request.getRequestURI().substring(zuulPrefix.length());
        final String method = request.getMethod();
        BaseContextHandler.setToken(null);
        // 不进行拦截的地址
        if (isStartWith(requestUri)) {
            return null;
        }
        IJWTInfo user = null;
        try {
            user = getJWTUser(request, ctx);
        } 
        catch (BaseException e) {
//        	e.printStackTrace();
            setFailedRequest(JSON.toJSONString(new TokenErrorResponse(e.getStatus(), e.getMessage())), 200);
            return null;
        }
        catch (Exception e) {
//        	e.printStackTrace();
            setFailedRequest(JSON.toJSONString(new TokenErrorResponse(e.getMessage())), 200);
            return null;
        }
        //控制后台管理权限
        if(user.getType() == JWTInfo.USER_TYPE_ADMIN) {
        	//需要不做权限控制接口
        	if(!isIgnorePermission(requestUri)) {
				List<PermissionApi> permissionIfs = adminUserService.getAllPermissionInfo();
				// 判断资源是否启用权限约束
				Stream<PermissionApi> stream = getPermissionIfs(requestUri, method, permissionIfs);
				List<PermissionApi> result = stream.collect(Collectors.toList());
				PermissionApi[] permissions = result.toArray(new PermissionApi[] {});
				if (permissions.length > 0) {
					checkUserPermission(permissions, ctx, user);
				}
				else{
                    log.error("未配置资源权限 requestUri[" + requestUri + "] method[" + method + "]");
                    setCurrentUserInfoAndLog(ctx, user, null);
                }
        	}
        }
        //商家端控制管理权限
        else if(user.getType() == JWTInfo.USER_TYPE_MERCHANTT) {
        	
        }
        //申请客户端密钥头
    	JWTInfo jwtInfo = new JWTInfo(CommonConstants.CLIENT_USER_NAME, JWTInfo.USER_TYPE_CLINET, CommonConstants.CLIENT_USER_USER_ID, CommonConstants.CLIENT_NICK_NAME, CommonConstants.CLIENT_REAL_NAME);
        ctx.addZuulRequestHeader(CommonConstants.AUTH_CLIENT_TOKEN_HEADER, JwtTokenUtil.generateToken(jwtInfo, CommonConstants.CLIENT_TOKEN_EXPIRE));
        return null;
    }

    /**
        * 获取目标权限资源
     * @param requestUri
     * @param method
     * @param serviceInfo
     * @return
     */
    private Stream<PermissionApi> getPermissionIfs(final String requestUri, final String method, List<PermissionApi> serviceInfo) {
        return serviceInfo.parallelStream().filter(new Predicate<PermissionApi>() {
            @Override
            public boolean test(PermissionApi permissionInfo) {
                String url = permissionInfo.getUri();
                String uri = url.replaceAll("\\{\\*\\}", "[a-zA-Z\\\\d]+");
                //String uri = url.replaceAll("\\{\\*\\}", "[0-9a-zA-Z\\/\\\\d]+");
                String regEx = "^" + uri + "$";                
                return Pattern.compile(regEx).matcher(requestUri).find() && method.equals(permissionInfo.getMethod());
            }
        });
    }

    private void setCurrentUserInfoAndLog(RequestContext ctx, IJWTInfo user, PermissionApi pm) {
        String host = ClientUtil.getClientIp(ctx.getRequest());
        ctx.addZuulRequestHeader("userId", user.getId());
        ctx.addZuulRequestHeader("userName", URLEncoder.encode(user.getNickName()));
        ctx.addZuulRequestHeader("userHost", ClientUtil.getClientIp(ctx.getRequest()));
        if(pm != null){
            LogApi logInfo = new LogApi(pm.getMenu(), pm.getName(), pm.getUri(), new Date(), user.getId(), user.getNickName(), host);
            DBLog.getInstance().setLogService(logService).offerQueue(logInfo);
        }
    }

    /**
     * 返回session中的用户信息
     * @param request
     * @param ctx
     * @return
     */
    private IJWTInfo getJWTUser(HttpServletRequest request, RequestContext ctx) throws Exception {
        String authToken = request.getHeader(CommonConstants.AUTH_USER_TOKEN_HEADER);
        if (StringUtils.isBlank(authToken)) {
            authToken = request.getParameter("token");
            log.error("request.getParameter authToken [" + authToken + "]");
        }
        if(StringUtils.isBlank(authToken)) {
            log.error("requestURI [" + request.getRequestURI() + "] token is null!");
            throw new UserTokenException("token is null or empty!");
        }
        ctx.addZuulRequestHeader(CommonConstants.AUTH_USER_TOKEN_HEADER, authToken);
        BaseContextHandler.setToken(authToken);
        //缓存检测
        IJWTInfo jWTInfo =  JwtTokenUtil.getInfoFromToken(authToken);
        if(jWTInfo.getType() == JWTInfo.USER_TYPE_ADMIN) {
        	boolean flag = adminUserService.validate(authToken);
        	if(!flag) {
                throw new BaseException(CommonConstants.USER_STATUS_ERROR, CommonConstants.USER_STATUS_ERROR_MESSAGE);
        	}
        }
        else if(jWTInfo.getType() == JWTInfo.USER_TYPE_BASE) {
        	ApiRestResponse<Boolean> apiRestResponse = userService.validate(authToken);
            log.info(apiRestResponse.toString());
        	if(apiRestResponse.getStatus() != CommonConstants.STATUS_OK_CODE) {
    			throw new BaseException(apiRestResponse.getStatus(), apiRestResponse.getMessage());
    		}
        	if(!apiRestResponse.getData().booleanValue()) {
                throw new BaseException(CommonConstants.USER_STATUS_ERROR, CommonConstants.USER_STATUS_ERROR_MESSAGE);
        	}
        }
        else if(jWTInfo.getType() == JWTInfo.USER_TYPE_MERCHANTT) {
        	boolean flag = merchantUserService.validate(authToken);
        	if(!flag) {
                throw new BaseException(CommonConstants.USER_STATUS_ERROR, CommonConstants.USER_STATUS_ERROR_MESSAGE);
        	}
        }
        return jWTInfo;
    }

    /**
     *  后台控制访问权限
     * @param permissions
     * @param ctx
     * @param user
     */
    private void checkUserPermission(PermissionApi[] allPermissions, RequestContext ctx, IJWTInfo user) {
        List<PermissionApi> myPermissions = adminUserService.getPermissionByUsername(user.getUniqueName());
        PermissionApi current = null;
        for (PermissionApi info : allPermissions) {
        	//验证每个权限
            boolean anyMatch = myPermissions.parallelStream().anyMatch(new Predicate<PermissionApi>() {
                @Override
                public boolean test(PermissionApi permissionInfo) {
                    return permissionInfo.getCode().equals(info.getCode());
                }
            });
            if (anyMatch) {
                current = info;
                break;
            }
        }
        if (current == null) {
        	log.error("调用checkUserPermission 无权限访问：" + ctx.getRequest().getRequestURI());
            setFailedRequest(JSON.toJSONString(new TokenForbiddenResponse(CommonConstants.TOKEN_FORBID_MSSSAGE)), 200);
        }
        else {
        	log.debug("调用checkUserPermission 有权限访问：" + ctx.getRequest().getRequestURI());
            if (!RequestMethod.GET.toString().equals(current.getMethod())) {
                setCurrentUserInfoAndLog(ctx, user, current);
            }
        }
    }


    /**
     * URI是否以什么打头
     * @param requestUri
     * @return
     */
    private boolean isStartWith(String requestUri) {
        boolean flag = false;
        for (String s : startWith.split(",")) {
            if (!StringUtils.isEmpty(s) && requestUri.startsWith(s.trim())) {
                return true;
            }
        }
        return flag;
    }
    
    /**
     * 是否为所有人可以访问的地址（不做权限控制）
     * @param requestUri
     * @return
     */
    private boolean isIgnorePermission(String requestUri) {
        boolean flag = false;
        for (String s : ignorePermission.split(",")) {
            if (!StringUtils.isEmpty(s) && requestUri.startsWith(s.trim())) {
                return true;
            }
        }
        return flag;
    }

    /**
     * 网关抛异常
     *
     * @param body
     * @param code
     */
    private void setFailedRequest(String body, int code) {
    	log.debug("Reporting error ({}): {}", code, body);
        RequestContext ctx = RequestContext.getCurrentContext();
        ctx.setResponseStatusCode(code);
        ctx.getResponse().setCharacterEncoding("UTF-8");
        if (ctx.getResponseBody() == null) {
            ctx.setResponseBody(body);
            ctx.setSendZuulResponse(false);
        }
    }
    
    public static void main(String[] args) {
    	String requestUri = "/admin/user/front/resetPwd/16";
        String url = "/admin/user/front/resetPwd";
        String uri = url.replaceAll("\\{\\*\\}", "[a-zA-Z\\\\d]+");
        String regEx = "^" + uri + "$";
        log.error("regEx="+regEx);

        Boolean flag1 = Pattern.compile(regEx).matcher(requestUri).find();
        Boolean flag2 = requestUri.startsWith(url + "/");
        log.error("requestUri="+requestUri);
        log.error("url="+url);
        log.error("flag1="+flag1);
        log.error("flag2="+flag2);            	
    }
}
